const fs = require('fs');
module.exports = class extends think.Controller {
  constructor(ctx) {
    super(ctx);
    this.ctx.targetHref = 'http://www.nhc.gov.cn/xcs/yqtb/list_gzbd.shtml'; // 目标网址
    this.ctx.listHrefReg = /(?<=<a href=").*?(?=" target="_blank" title='截至)/g; // 列表里的url提取规则
    this.ctx.contentReg = null; // 内容页的提取规则(缩小范围，使得精确度更高)
    this.ctx.dataRegs = [
      { name: '截至时间', key: 'k0', reg: /(?<=截至)\d{1,2}月\d{1,2}日/ },
      { name: '累计确诊', key: 'k1', reg: /(?<=累计[^\d]*确诊[^\d]*)\d+/ },
      { name: '累计治愈', key: 'k2', reg: /(?<=累计[^\d]*治愈[^\d]*)\d+/ },
      { name: '累计死亡', key: 'k3', reg: /(?<=累计[^\d]*死亡[^\d\s]*)\d+/ },
      { name: '现存疑似', key: 'k4', reg: /(?<=疑似[^\d]*)\d+/ },
      { name: '现存确诊', key: 'k5', reg: /(?<=现(有|存)[^\d\s]*确诊[^\d\s]*)\d+/ },
      { name: '现存重症', key: 'k6', reg: /(?<=重症[^\d]*)\d+/ },
      { name: '累计追踪', key: 'k7', reg: /(?<=追踪[^\d]*)\d+/ },
      { name: '医学观察', key: 'k8', reg: /(?<=医学观察[^\d]*)\d+/ }
    ];
    this.ctx.targetHost = 'http://www.nhc.gov.cn';  // 相对路径需要加上的域名
    this.ctx.timeStep = 0;  // 抓取间隔
    this.ctx.filePath = '/www/data.json'; // 数据存放路径
  }
  /**
   * 获取已保存的数据
   */
  async getDataAction() {
    let data = await this.getFileData();
    if (!data) {
      return this.fail('暂无数据！');
    }
    try {
      data = JSON.parse(data);
    }
    catch(err) {
      return this.fail('数据格式错误！');
    }
    return this.success(data, '请求成功！');
  }

  /**
   * 获取所有的URL数据
   */
  async updateDataAction() {
    // 获取带爬取的所有URL
    const urls = await this.getAllUrls(); 
    if (think.isEmpty(urls)) {
      return this.fail('列表页URLS获取失败！');
    }
    // 获取爬取的内容
    const data = await this.getAllContent(urls);
    if (think.isEmpty(data)) {
      return this.fail('数据获取失败！');
    }
    // 保存数据到文件
    this.saveData(data);
    return this.success(data, '请求成功！');
  }


  /**
   * 获取文件数据
   */
  async getFileData() {
    var content = null;
    const filePath = `${think.ROOT_PATH}${this.ctx.filePath}`;
    const hasFile = think.isFile(filePath);
    if (hasFile) {
      const readFilePromise = think.promisify(fs.readFile, fs);
      content = await readFilePromise(filePath, 'utf8');
    }
    return content;
  }
  /**
   * 将数据写入文件
   */
  async saveData(data) {
    if (!data) {
      return;
    }
    const filePath = `${think.ROOT_PATH}${this.ctx.filePath}`;
    const writeFilePromise = think.promisify(fs.writeFile, fs);
    const res = await writeFilePromise(filePath, JSON.stringify(data));
    if (think.isEmpty(res)) {
      console.log('数据写入成功！', filePath);
    } else {
      console.log('数据写入失败！');
    }
  }

  /**
   * 获取需要爬去的所有的url（列表页的超链接）
   */
  async getAllUrls() {
    
    const html = await this.getHtml(this.ctx.targetHref); // 先获取列表的html字符串
    if (html.fail) { // 如果获取失败，返回空
      return null;
    }
    let arr = html.match(this.ctx.listHrefReg);  // 拿到相对路径的数组 ['']
    // 如果获取到相对URL，循环拼接域名
    if (!think.isEmpty(arr)) {
      arr = arr.map(v => {
        return  `${this.ctx.targetHost}${v}`
      });
      console.log(arr);
      console.log('======列表URL找寻完成======');
    } else {
      console.log('======列表URL找寻失败======');
    }
    return arr;
  }

  /**
   * 获取所有的内容
   * @param {*} urls Array 待爬取内容的url
   * [
   *  {k0: 111, ......},
   * {k0: 111, ......},
   * {k0: 111, ......}
   * ]
   */
  async getAllContent(urls) {
    console.log('======内容找寻开始======');
    const arr = []; // 待返回的数据
    for (const url of urls) {
      const data = await this.getOneContent(url);
      if (data) {
        arr.push(data);
      }
    }
    console.log('======内容找寻结束======');
    return think.isEmpty(arr) ? null : arr; 
  }

  /**
   * 获取页面的内容
   * 
   * {
   *  k0: 123,
   *  k1: 12345
   * ........
   * }
   */
  async getOneContent(url) {
    let next = await this.canDoNext();
    if (next) {
      let obj = {}; // 待返回的记录
      const html = await this.getHtml(url);
      if (html) {
        // 获取我们需要分析的精准数据
        let minStr = /(?<=<br>\s*)截至.*?(?=<br>)/.exec(html);
        if (!minStr) {
          return obj;
        }
        this.ctx.dataRegs.forEach(v => {
          let num = v.reg.exec(minStr[0]); 
          if (num) {
            obj[v.key] = num[0];
          }
        });
        console.log('抓取成功', url);
      } else {
        console.log('抓取失败', url);
      }
      return obj;
    }
  }
  /**
   * 执行下一次获取
   */
  async canDoNext() {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(true);
      }, this.ctx.timeStep);
    })
  }
  /**
   * 获取传入的url的html文档
   * @param {*} url string
   */
  async getHtml(url) {
    const htmlService = think.service('html');
    const res = await htmlService.getHtml(url);
    return res;
  }
};
